package com.net.xpay.core.service.impl.safePay;

import com.alipay.api.AlipayApiException;
import com.alipay.api.domain.AlipayEbppIndustryJobPayslipSyncModel;
import com.alipay.api.response.AlipayFundAccountbookNotifySubscribeResponse;
import com.alipay.api.response.AlipayFundTransCommonQueryResponse;
import com.alipay.api.response.AlipayFundTransUniTransferResponse;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.net.xpay.alipay.enums.ChannelType;
import com.net.xpay.common.domain.*;
import com.net.xpay.common.domain.notify.SafePayBatchNotifyRecord;
import com.net.xpay.common.domain.partner.PartnerCode;
import com.net.xpay.common.enums.DataSnapType;
import com.net.xpay.common.enums.notify.NotifyStatus;
import com.net.xpay.common.enums.safePay.UniTransStatus;
import com.net.xpay.common.enums.user.CheckCodeType;
import com.net.xpay.common.enums.user.ServicePayMode;
import com.net.xpay.common.helper.NoticeHelper;
import com.net.xpay.common.helper.RedisHelper;
import com.net.xpay.common.manager.*;
import com.net.xpay.core.constant.*;
import com.net.xpay.core.enums.*;
import com.net.xpay.core.model.*;
import com.net.xpay.core.service.AlipayEbppIndustryJobPayslipSync;
import com.net.xpay.core.service.safePay.CollectionSafePayService;
import com.net.xpay.core.validator.BookAccountTransferOrderValidator;
import com.net.xpay.core.validator.SafePayBatchValidator;
import com.net.common.annotation.WriteTransactional;
import com.net.common.concurrent.ThreadUtil;
import com.net.common.exception.BusinessException;
import com.net.common.util.*;
import com.net.xpay.alipay.enums.IdentityType;
import com.net.xpay.alipay.model.AlipayFundAccountbookPageCreateModel;
import com.net.xpay.alipay.safePay.PoseidonSafePayAlipayClient;
import com.net.xpay.common.domain.*;
import com.net.xpay.common.manager.notify.SafePayBatchNotifyRecordManager;
import com.net.xpay.common.manager.partner.PartnerCodeManager;
import com.net.xpay.common.service.CheckCodeService;
import com.net.xpay.core.domain.AccountDetail;
import com.net.xpay.core.domain.UserAccount;
import com.net.xpay.core.domain.safePay.BookAccountChargeOrder;
import com.net.xpay.core.domain.safePay.SafePayBatch;
import com.net.xpay.core.domain.safePay.SafePayBookAccount;
import com.net.xpay.core.domain.safePay.SafePayOrder;
import com.net.xpay.core.enums.*;
import com.net.xpay.core.helper.AccountDetailHelper;
import com.net.xpay.core.manager.AccountDetailManager;
import com.net.xpay.core.manager.UserAccountManager;
import com.net.xpay.core.manager.safePay.BookAccountChargeOrderManager;
import com.net.xpay.core.manager.safePay.SafePayBatchManager;
import com.net.xpay.core.manager.safePay.SafePayBookAccountManager;
import com.net.xpay.core.manager.safePay.SafePayOrderManager;
import com.net.xpay.core.model.*;
import com.net.xpay.core.service.AccountService;
import com.net.xpay.core.service.safePay.SafePayInnerService;
import com.net.xpay.core.service.safePay.SafePayService;
import com.net.xpay.core.utils.GenIdUtil;
import com.net.xpay.core.utils.MoneyCheckUtil;
import com.net.shard.ShardContextHolder;
import com.net.shard.ShardUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;
import java.util.function.Function;

/**
 * 注意事务不要加在类上 哪个方法要用事务自己加
 */
@Slf4j
@Service
public class SafePayServiceImpl implements SafePayService {
    @Autowired
    private CollectionSafePayService collectionSafePayService;

    @Autowired
    private SafePayBookAccountManager safePayBookAccountManager;

    @Autowired
    private PoseidonSafePayAlipayClient xpaySafePayAlipayClient;

    @Autowired
    private BookAccountChargeOrderManager bookAccountChargeOrderManager;

    @Autowired
    private SafePayBatchValidator safePayBatchValidator;

    @Autowired
    private NoticeHelper noticeHelper;

    @Autowired
    private UserAccountManager userAccountManager;

    @Autowired
    private SafePayBatchManager safePayBatchManager;

    @Autowired
    private SafePayOrderManager safePayOrderManager;

    @Autowired
    private AccountDetailHelper accountDetailHelper;

    @Autowired
    private DataSnapManager dataSnapManager;

    @Autowired
    private AccountService accountService;

    @Autowired
    private SafePayInnerService safePayInnerService;

    @Autowired
    private UserProfileManager userProfileManager;

    @Autowired
    private UserManager userManager;

    @Autowired
    private CheckCodeService checkCodeService;

    @Autowired
    private AccountDetailManager accountDetailManager;

    @Autowired
    private PartnerCodeManager partnerCodeManager;

    @Autowired
    private RedisHelper redisHelper;

    @Autowired
    private BookAccountTransferOrderValidator bookAccountTransferOrderValidator;

    @Autowired
    private BookAccountTransferOrderManager bookAccountTransferOrderManager;

    @Autowired
    private BookAccountLargeDepositChargeOrderManager bookAccountLargeDepositChargeOrderManager;

    @Autowired
    private SafePayBatchNotifyRecordManager safePayBatchNotifyRecordManager;

    @Autowired
    private AlipayEbppIndustryJobPayslipSync alipayEbppIndustryJobPayslipSync;

    private final ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
            .setNameFormat("safe-pay-batch-update-%d").build();

    //Common Thread Pool
    private final ExecutorService pool = new ThreadPoolExecutor(16, 16,
            0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<>(20000), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());


    @Override
    @WriteTransactional
    public BookAccountChargeOrder createBookAccountChargeOrder(User user, BookAccountChargeOrderModel model) {
        if (user == null) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "用户不能为空");
        }

        if (model == null) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "充值订单参数不能为空");
        }

        if (Strings.isNullOrEmpty(model.getImportBizNo())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "外部订单号不能为空");
        }

        BookAccountChargeOrder selected = bookAccountChargeOrderManager.findByImportBizNo(user.getId(), model.getImportBizNo());
        if (selected != null) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "外部订单号已被使用：" + model.getImportBizNo());
        }

        if (!AlipayAccountCheckUtil.isEffectiveAccount(model.getAlipayNickName())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "支付宝账号格式错误");
        }

        if (Strings.isNullOrEmpty(model.getTransAmount())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "转账金额不能为空");
        }

        if (!MoneyCheckUtil.isValidMoney(model.getTransAmount(), 2)) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "转账金额格式错误，最多2位小数");
        }

        if (new BigDecimal(model.getTransAmount()).compareTo(new BigDecimal(0)) <= 0) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "充值金额必须大于0元");
        }

        SafePayBookAccount safePayBookAccount = safePayBookAccountManager.findByAlipayNickName(user.getId(), model.getAlipayNickName());
        UserProfile userProfile = userProfileManager.getById(user.getId());

//        if (Boolean.TRUE.equals(userProfile.getOpenSafePayNewSignMethod())) {
//            if (safePayBookAccount.getSignMethod() == SafePayBookAccountSignMethod.OLD) {
//                throw new BusinessException(PoseidonErrorCode.NEED_SAFE_PAY_RESIGN, "当前无法充值本金，为了保障你的账号安全，需要重新签约。请先解约本账号，然后重新签约。如果账号内本金不为0，请先将本金余额转出，然后解约后再重新签约。");
//            }
//        }

        Preconditions.checkArgument(safePayBookAccount.getAlipayNickName().equals(model.getAlipayNickName()));
        if (safePayBookAccount.getBookId() == null || !SafePayBookAccountBookStatus.YES.equals(safePayBookAccount.getBookStatus())) {
            log.warn("createBookAccountChargeOrder error bookId null|model={}|account={}", SerializationUtil.obj2String(model), SerializationUtil.obj2String(safePayBookAccount));
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "尚未开通资金账本，无法充值");
        }

        if (!Boolean.FALSE.equals(userProfile.getNeedSafePayAuth())) {
            if (safePayBookAccount.getAuditStatus() == SafePayBookAccountAuditStatus.INIT || safePayBookAccount.getAuditStatus() == SafePayBookAccountAuditStatus.FAIL) {
                throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "企业支付宝尚未通过认证，请先认证");
            }

            if (safePayBookAccount.getAuditStatus() == SafePayBookAccountAuditStatus.AUDITING) {
                throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "企业支付宝认证尚在审核中，请耐心等待");
            }
        }


        BookAccountChargeOrder order = BookAccountChargeOrder.getInstance();
        order.setUserId(user.getId());
        order.setSource("AUTO");
        order.setAlipayNickName(model.getAlipayNickName());
        order.setAlipayUid(safePayBookAccount.getAlipayUid() == null ? "" : safePayBookAccount.getAlipayUid());
        order.setTransAmount(new BigDecimal(model.getTransAmount()).setScale(2, BigDecimal.ROUND_HALF_UP));
        order.setSafePayBookAccountId(safePayBookAccount.getBookId());
        order.setBookId(safePayBookAccount.getBookId());
        order.setAgreementNo(safePayBookAccount.getAgreementNo());
        order.setImportBizNo(model.getImportBizNo());
        order.setOrderTitle("充值");
        order.setRemark(model.getImportBizNo());
        order.setStatus(BookAccountChargeOrderStatus.WAIT_PAY);
        order.setTimeExpire(LocalDateTime.now().plusMinutes(30L));//30分钟过期

        bookAccountChargeOrderManager.save(order);

        String payUrl = xpaySafePayAlipayClient.alipayFundTransPagePayFundAccountBook(order);
        order.setPayUrl(payUrl);
        bookAccountChargeOrderManager.updateByPrimaryKeyWithVersion(order);

        return order;
    }

    @Override
    public BookAccountChargeOrder notifyCreateBookAccountChargeOrder(BookAccountChargeOrderNotifyModel model) {
        Preconditions.checkArgument(!Strings.isNullOrEmpty(model.getBookId()) && !Strings.isNullOrEmpty(model.getTransAmount()));
        SafePayBookAccount safePayBookAccount = safePayBookAccountManager.getByBookId(model.getBookId());
        if (safePayBookAccount == null) {
            return null;
        }
        BookAccountChargeOrder selectedOrder = bookAccountChargeOrderManager.findByImportBizNo(safePayBookAccount.getUserId(), model.getSource() + "-" + model.getOrderNo());
        if (selectedOrder != null) {
            log.info("notifyCreateBookAccountChargeOrder order exist|id={}|userId={}", selectedOrder.getId(), selectedOrder.getUserId());
            return selectedOrder;
        }

        BookAccountChargeOrder order = BookAccountChargeOrder.getInstance();
        order.setUserId(safePayBookAccount.getUserId());
        order.setAlipayNickName(safePayBookAccount.getAlipayNickName());
        order.setAlipayUid(safePayBookAccount.getAlipayUid() == null ? "" : safePayBookAccount.getAlipayUid());
        order.setTransAmount(new BigDecimal(model.getTransAmount()).setScale(2, BigDecimal.ROUND_HALF_UP));
        order.setSafePayBookAccountId(safePayBookAccount.getBookId());
        order.setBookId(safePayBookAccount.getBookId());
        order.setAgreementNo(safePayBookAccount.getAgreementNo());
        order.setImportBizNo(model.getSource() + "-" + model.getOrderNo());
        order.setOrderTitle("汇款充值");
        order.setSource(model.getSource());
        order.setPayerIdentityType(model.getPayerIdentityType());
        order.setPayerIdentity(model.getPayerIdentity());
        order.setPayerName(model.getPayerName());
        order.setRemark(order.getImportBizNo());
        order.setStatus(BookAccountChargeOrderStatus.SUCCESS);
        order.setTimeExpire(LocalDateTime.now().plusMinutes(30L));//30分钟过期
        order.setOrderId(model.getOrderNo());
        order.setPayFundOrderId(model.getOrderNo());
        order.setEndTime(LocalDateTime.now());
        order.setPayDate(LocalDateUtil.getDateTimeAsString(order.getEndTime()));

        bookAccountChargeOrderManager.save(order);

        //公共库存一条汇款记录
        BookAccountLargeDepositChargeOrder largeDepositChargeOrder = BookAccountLargeDepositChargeOrder.getInstance();
        largeDepositChargeOrder.setUserId(order.getUserId());
        largeDepositChargeOrder.setAlipayNickName(order.getAlipayNickName());
        largeDepositChargeOrder.setAlipayName(safePayBookAccount.getAlipayName());
        largeDepositChargeOrder.setAlipayUid(order.getAlipayUid());
        largeDepositChargeOrder.setTransAmount(order.getTransAmount());
        largeDepositChargeOrder.setSafePayBookAccountId(order.getSafePayBookAccountId());
        largeDepositChargeOrder.setBookId(order.getBookId());
        largeDepositChargeOrder.setAgreementNo(order.getAgreementNo());
        largeDepositChargeOrder.setEndTime(order.getEndTime());
        largeDepositChargeOrder.setOrderNo(model.getOrderNo());
        largeDepositChargeOrder.setPayerIdentityType(order.getPayerIdentityType());
        largeDepositChargeOrder.setPayerIdentity(order.getPayerIdentity());
        largeDepositChargeOrder.setSource(order.getSource());
        largeDepositChargeOrder.setPayerName(order.getPayerName());

        bookAccountLargeDepositChargeOrderManager.save(largeDepositChargeOrder);
        return order;
    }

    @Override
    @WriteTransactional
    public void updateBookAccountChargeOrder(Long id) {
        Preconditions.checkArgument(id != null);
        BookAccountChargeOrder order = bookAccountChargeOrderManager.getById(id);
        if (BookAccountChargeOrderStatus.WAIT_PAY != order.getStatus()) {
            return;
        }

        if (order.getCreateTime().isBefore(LocalDateTime.now().minusDays(7L))) {
            order.setStatus(BookAccountChargeOrderStatus.CLOSED);
            bookAccountChargeOrderManager.updateByPrimaryKeyWithVersion(order);
        }
        try {
            AlipayFundTransCommonQueryResponse resp = xpaySafePayAlipayClient.queryAccountBookCharge(order.getOutBizNo());
            if (!resp.isSuccess()) {
                log.info("queryAccountBookCharge failed|id={}|resp={}", id, SerializationUtil.obj2String(resp));
                //过了1小时的订单直接给关闭了
                if ("ORDER_NOT_EXIST".equals(resp.getSubCode()) && order.getCreateTime().isBefore(LocalDateTime.now().minusHours(1L))) {
                    order.setStatus(BookAccountChargeOrderStatus.CLOSED);
                    bookAccountChargeOrderManager.updateByPrimaryKeyWithVersion(order);
                }
                return;
            }
            if (BookAccountChargeOrderStatus.of(resp.getStatus()) == BookAccountChargeOrderStatus.WAIT_PAY) {
                return;
            }

            order.setStatus(BookAccountChargeOrderStatus.of(resp.getStatus()));
            order.setOrderId(resp.getOrderId());
            order.setPayFundOrderId(resp.getPayFundOrderId());
            order.setPayDate(resp.getPayDate());
            order.setEndTime(LocalDateTime.now());
            bookAccountChargeOrderManager.updateByPrimaryKeyWithVersion(order);
        } catch (AlipayApiException e) {
            log.error("updateBookAccountChargeOrder error|id={}", id, e);
        }
    }

    @Override
    @WriteTransactional
    public SafePayBatch createSafePayBatch(User user, SafePayBatchCreateModel model) {
        UserAccount userAccount = userAccountManager.getByIdWithLock(user.getId());
        safePayBatchValidator.validateCreateBatch(user, model);

        SafePayBookAccount safePayBookAccount = safePayBookAccountManager.findByAlipayNickName(user.getId(), model.getAlipayNickName());
        //检查下用户的资金账本
        checkSafePayBookAccount(safePayBookAccount);
        //保存转账批次
        SafePayBatch batch = doCreateSafePayBatch(userAccount, safePayBookAccount, user, model);
        //保存转账明细
        createSafePayOrder(batch, model.getPayTrans());
        //存下推送记录
        processNotifyRecord(user, batch, model.getNotifyUrl(), model);
        //扣钱
        payServiceMoney(user, userAccount, batch);

        //判断是否需要直接确认转账 适合不需要短信等验证的
        if (model.getConfirmTrans() != null && model.getConfirmTrans() == 1) {
            SafePayBatchTransModel safePayBatchTransModel = new SafePayBatchTransModel();
            safePayBatchTransModel.setImportBatchNo(model.getImportBatchNo());
            transSafePayBatch(user, safePayBatchTransModel);
        }

        return batch;
    }

    private void payServiceMoney(User user, UserAccount userAccount, SafePayBatch batch) {
        if (batch.getFree()) {
            //扣除免费次数
            AccountDetail accountDetail = AccountDetail.getInstance(user.getId(), batch.getId());
            accountDetailHelper.initTransBatchAccountDetail(accountDetail, AccountBillType.SAFE_PAY_TRANS_SERVICE_PRICE, -1L, "安全付批量转账扣除批次次数");
            accountService.onPay(user, userAccount, accountDetail);
            return;
        }
        //预扣服务费
        AccountDetail accountDetail = AccountDetail.getInstance(user.getId(), batch.getId());
        accountDetailHelper.initCommonAccountDetail(accountDetail, AccountBillType.SAFE_PAY_TRANS_SERVICE_PRICE, -batch.getServicePrice(), "安全付批量转账扣除余额");

        accountService.onPay(user, userAccount, accountDetail);

        batch.setHasPayService(true);
        safePayBatchManager.updateByPrimaryKeyWithVersion(batch);

    }

    private void createSafePayOrder(SafePayBatch batch, List<SafePayTransModel> payTrans) {
        List<SafePayOrder> safePayOrders = new ArrayList<>();
        for (SafePayTransModel model : payTrans) {
            SafePayOrder order = SafePayOrder.getInstance();
            order.setUserId(batch.getUserId());
            order.setPartnerId(batch.getPartnerId());
            order.setFatherPartnerId(batch.getFatherPartnerId());
            order.setFree(batch.getFree());
            order.setAlipayNickName(batch.getAlipayNickName());
            order.setSafePayBatchId(batch.getId());
            order.setImportBatchNo(batch.getImportBatchNo());
            order.setSafePayBookAccountId(batch.getSafePayBookAccountId());
            order.setBookId(batch.getBookId());
            order.setAgreementNo(batch.getAgreementNo());
            order.setImportBizNo(model.getImportBizNo());
            order.setTransAmount(new BigDecimal(model.getTransAmount()));
            order.setOrderTitle(Strings.isNullOrEmpty(model.getOrderTitle()) ? "酬劳发放" : model.getOrderTitle());
            order.setRemark(model.getRemark());
            order.setIdentity(model.getIdentity());
            if (PayeeType.BANK.equals(batch.getType())) {
                order.setIdentityType("BANKCARD_ACCOUNT"); //银行卡号
            } else {
                if (Strings.isNullOrEmpty(model.getIdentityType())) {
                    order.setIdentityType("ALIPAY_LOGON_ID");//支付宝账号
                } else {
                    order.setIdentityType(model.getIdentityType());
                }
            }

            order.setName(!Strings.isNullOrEmpty(model.getName()) ? model.getName() : "");
            order.setStatus(SafePayOrderStatus.WAIT_ENSURE);
            safePayOrders.add(order);
        }

        safePayOrderManager.batchSave(safePayOrders);
    }

    private SafePayBatch doCreateSafePayBatch(UserAccount userAccount, SafePayBookAccount safePayBookAccount, User user, SafePayBatchCreateModel model) {
        //检查下手续费余额够不够
        long estimateFee = checkHasEnoughMoneyAndReturnEstimateFee(userAccount, user, model);

        SafePayBatch batch = SafePayBatch.getInstance();
        batch.setUserId(user.getId());
        batch.setPartnerId(user.getPartnerId());
        batch.setFatherPartnerId(user.getFatherPartnerId());
        batch.setFree(userAccount.getTransBatchLeft() > 0L && new BigDecimal(model.getTotalTransAmount()).compareTo(CoreConstant.MAX_TRANS_FREE_AMOUNT) <= 0);
        batch.setNeedNotify(model.getNeedNotify() != null ? model.getNeedNotify() : false);
        batch.setType(model.getType());
        batch.setAlipayNickName(model.getAlipayNickName());
        batch.setSafePayBookAccountId(safePayBookAccount.getId());
        batch.setBookId(safePayBookAccount.getBookId());
        batch.setAgreementNo(safePayBookAccount.getAgreementNo());
        batch.setImportBatchNo(model.getImportBatchNo());
        batch.setHasPayService(false);
        batch.setServicePrice(estimateFee);
        batch.setRefund(0L);
        batch.setFinalServicePrice(0L);
        batch.setMode(user.getMode());
        batch.setCommissionMode(user.getCommissionMode());
        batch.setCostServiceRatio(new BigDecimal(0));
        batch.setServiceRatio(user.getServiceRatio());
        batch.setServiceUnitPrice(user.getServiceUnitPrice());
        if (PayeeType.BANK.equals(model.getType())) {
            //到卡服务费 由于之前没有到卡的单独字段 所以兼容一下以前的数据
            if (user.getBankServiceRatio() != null) {
                batch.setServiceRatio(user.getBankServiceRatio());
            }
            if (user.getBankServiceUnitPrice() != null) {
                batch.setServiceUnitPrice(user.getBankServiceUnitPrice());
            }
        }
        batch.setTotalTransAmount(StreamUtil.sum(model.getPayTrans(), (Function<SafePayTransModel, BigDecimal>) safePayTransModel -> new BigDecimal(safePayTransModel.getTransAmount())));
        batch.setTotalCount((long) model.getPayTrans().size());
        batch.setSuccessAmount(new BigDecimal("0.00"));
        batch.setSuccessCount(0L);
        batch.setFailedAmount(new BigDecimal("0.00"));
        batch.setFailCount(0L);
        batch.setStatus(SafePayBatchStatus.WAIT_ENSURE);
        batch.setUnitPartnerBenefit(user.getPartnerBenefit());
        batch.setUnitFatherPartnerBenefit(user.getFatherPartnerBenefit());
        batch.setBenefitRatio(user.getBenefitRatio());
        batch.setFatherBenefitRatio(user.getFatherBenefitRatio());
        batch.setTotalPartnerBenefit(0L);
        batch.setTotalFatherPartnerBenefit(0L);
        batch.setTotalPlatformBenefit(0L);
        batch.setNeedEnsure(false);

        if (!Strings.isNullOrEmpty(user.getPma())) {
            PartnerCode partnerCode = partnerCodeManager.findByPma(user.getPma());

            batch.setCommissionMode(user.getCommissionMode());
            //安全付到银行卡费率单独计算
            if (PayeeType.BANK.equals(batch.getType())) {
                Preconditions.checkArgument(partnerCode.getSafePayBankCostServiceRatio() != null);
                BigDecimal bankCostServiceRatio = user.getBankCostServiceRatio();
                if (bankCostServiceRatio == null) {
                    bankCostServiceRatio = partnerCode.getSafePayBankCostServiceRatio();
                }
                batch.setCostServiceRatio(bankCostServiceRatio);
            } else {
                BigDecimal costServiceRatio = user.getCostServiceRatio();
                if (costServiceRatio == null) {
                    costServiceRatio = partnerCode.getCostServiceRatio();
                }
                batch.setCostServiceRatio(costServiceRatio);
            }
        }

        //保险起见 再核对一遍
        if (batch.getTotalTransAmount().compareTo(new BigDecimal(model.getTotalTransAmount())) != 0) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "批次总金额不匹配");
        }

        if (Boolean.TRUE.equals(model.getBatchEnsure())) {
            batch.setNeedEnsure(true);
        } else {
            batch.setNeedEnsure(false);
        }

        safePayBatchManager.save(batch);

        return batch;
    }

    private void checkSafePayBookAccount(SafePayBookAccount safePayBookAccount) {
        if (safePayBookAccount == null) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "付款支付宝账号不存在");
        }
        if (safePayBookAccount.getBookId() == null || !SafePayBookAccountBookStatus.YES.equals(safePayBookAccount.getBookStatus())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "尚未开通资金账本，无法转账");
        }

        UserProfile userProfile = userProfileManager.getById(safePayBookAccount.getUserId());
        if (!Boolean.FALSE.equals(userProfile.getNeedSafePayAuth())) {
            if (safePayBookAccount.getAuditStatus() == SafePayBookAccountAuditStatus.INIT || safePayBookAccount.getAuditStatus() == SafePayBookAccountAuditStatus.FAIL) {
                throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "企业支付宝尚未通过认证，请先认证");
            }

            if (safePayBookAccount.getAuditStatus() == SafePayBookAccountAuditStatus.AUDITING) {
                throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "企业支付宝认证尚在审核中，请耐心等待");
            }
        }
    }

    @Override
    @WriteTransactional
    public SafePayBatch transSafePayBatch(User user, SafePayBatchTransModel model) {
        SafePayBatch batch = safePayBatchValidator.validateTransSafePayBatch(user, model);

        Preconditions.checkArgument(batch.getUserId().equals(user.getId()) && batch.getImportBatchNo().equals(model.getImportBatchNo()));

        if (!SafePayBatchStatus.WAIT_ENSURE.equals(batch.getStatus())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "当前批次已处理，请勿重复操作");
        }

        batch.setStatus(SafePayBatchStatus.WAIT_TRANS);
        batch.setStartTime(LocalDateTime.now());
        safePayBatchManager.updateByPrimaryKeyWithVersion(batch);

        safePayOrderManager.updateStatusByBatchId(user.getId(), batch.getId(), SafePayOrderStatus.WAIT_TRANS, batch.getFree(), null);

        return batch;
    }

    private void processNotifyRecord(User user, SafePayBatch batch, String notifyUrl, SafePayBatchCreateModel model) {
        UserProfile userProfile = userProfileManager.getById(user.getId());
        if (!Boolean.TRUE.equals(batch.getNeedNotify())) {
            return;
        }

        if (Strings.isNullOrEmpty(notifyUrl)) {
            notifyUrl = userProfile.getSafePayNotifyUrl();
        }

        if (Strings.isNullOrEmpty(notifyUrl)) {
            return;
        }

        SafePayBatchNotifyRecord record = SafePayBatchNotifyRecord.getInstance();
        record.setUserId(batch.getUserId());
        record.setAppId(model.getAppId());
        record.setImportBatchNo(batch.getImportBatchNo());
        record.setSafePayBatchId(batch.getId());
        record.setStatus(NotifyStatus.INIT);
        record.setNotifyCount(0);
        record.setNextNotifyTime(LocalDateTime.now());
        record.setNotifyUrl(notifyUrl);

        safePayBatchNotifyRecordManager.save(record);
    }

    /**
     * 后台处理转账 不加事务
     */
    @Override
    public void processTransSafePayBatch(Long safePayBatchId) throws ExecutionException, InterruptedException {
        SafePayBatch safePayBatch = safePayBatchManager.getById(safePayBatchId);
        if (safePayBatch.getStatus() != SafePayBatchStatus.WAIT_TRANS) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "批次状态不正确");
        }

        List<SafePayOrder> orders = safePayOrderManager.listByBatchId(safePayBatch.getUserId(), safePayBatchId);
        User  user =  userManager.getById(safePayBatch.getUserId());
        long t = System.currentTimeMillis();
        List<Future<Void>> futures = new ArrayList<>();
        final long shardId = ShardContextHolder.getShardContext().getShardId();

        for (SafePayOrder order : orders) {
            if (order.getStatus() != SafePayOrderStatus.WAIT_TRANS) {
                continue;
            }
            if (order.getStartTime() == null) {
                order.setStartTime(LocalDateTime.now());
            }
            Future<Void> future = pool.submit(() -> {
                try {
                    //子线程需要重设一下分片id
                    ShardUtil.setShardContext(shardId);
                    transOrder(order);
                    if (order.getStatus() != SafePayOrderStatus.DEALING) {
                        Date now = new Date();
                        LocalDateTime localDateTimeNow = now.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
                        order.setEndTime(localDateTimeNow);

                        // 到账啦接口调用
                        if("ALIPAY_LOGON_ID".equals(order.getIdentityType())){
                            try {
                                AlipayEbppIndustryJobPayslipSyncModel syncModel = new AlipayEbppIndustryJobPayslipSyncModel();
                                syncModel.setOutBizNo(order.getId().toString());
                                syncModel.setCompanyName(user.getName());
                                syncModel.setAmount(order.getTransAmount().toString());
                                syncModel.setSalaryTime(now);
                                syncModel.setChannel(ChannelType.ALIPAY.getValue());
                                syncModel.setLoginId(order.getIdentity());
                                syncModel.setRemark(order.getOrderTitle());
                                syncModel.setOrderId(order.getOrderId());
                                syncModel.setUserName(order.getName());
                                alipayEbppIndustryJobPayslipSync.payslipSync(syncModel);
                            } catch (Exception e) {
                                log.error("到账啦接口异常:", e);
                            }
                        }

                        // 到账啦接口调用  每周一三五发10笔
//                        if("ALIPAY_LOGON_ID".equals(order.getIdentityType())&&
//                                PayslipAtomicInteger.getNum()<11){
//                            LocalDateTime nowTime = LocalDateTime.now();
//                            DayOfWeek dayOfWeek = nowTime.getDayOfWeek();
//                            if (dayOfWeek == DayOfWeek.MONDAY
//                                    || dayOfWeek == DayOfWeek.WEDNESDAY
//                                    || dayOfWeek == DayOfWeek.FRIDAY) {
//                                try {
//                                    AlipayEbppIndustryJobPayslipSyncModel syncModel = new AlipayEbppIndustryJobPayslipSyncModel();
//                                    syncModel.setOutBizNo(order.getId().toString());
//                                    syncModel.setCompanyName(user.getName());
//                                    syncModel.setAmount(order.getTransAmount().toString());
//                                    syncModel.setSalaryTime(now);
//                                    syncModel.setChannel(ChannelType.ALIPAY.getValue());
//                                    syncModel.setLoginId(order.getIdentity());
//                                    syncModel.setRemark(order.getOrderTitle());
//                                    syncModel.setOrderId(order.getOrderId());
//                                    syncModel.setUserName(order.getName());
//                                    alipayEbppIndustryJobPayslipSync.payslipSync(syncModel);
//                                    int num = PayslipAtomicInteger.incrementNum();
//                                    log.info("到账啦接口通知："+order.getIdentity()+",第"+num+"笔");
//                                } catch (Exception e) {
//                                    log.error("到账啦接口异常:", e);
//                                }
//                            }
//                        }
                    }
                    safePayOrderManager.updateByPrimaryKeyWithVersion(order);
                } catch (Exception e) {
                    log.error("startTransSafePayBatch error|order={}", SerializationUtil.obj2String(order), e);
                    //中止处理 处理bug
                    noticeHelper.sendSystemMonitorNotice("安全付转账订单接口调用发生未知错误，请尽快修复问题！订单id=" + order.getId() + "，商户编号：" + order.getUserId(), "text");
                    throw e;
                } finally {
                    ShardUtil.cleanShardContext();
                }
                return null;
            });
            futures.add(future);
        }

        //同步一下
        for (Future<Void> f : futures) {
            try {
                f.get();
            } catch (Exception e) {
                log.info("processTransSafePayBatch error|safePayBatchId={}", safePayBatchId, e);
                throw e;
            }
        }
        log.info("processTransSafePayBatch finish safePayBatchId={}|cost={}", safePayBatchId, System.currentTimeMillis() - t);
        //银行卡类型
        if (PayeeType.BANK.equals(safePayBatch.getType())) {
            processOnBankTrans(safePayBatch);
        } else {
            processOnFinalTrans(safePayBatch);
        }
    }

    @Override
    public void updateDealingBankBatch(Long safePayBatchId) {
        SafePayOrderSearchModel searchModel = new SafePayOrderSearchModel();
        searchModel.setStatus(SafePayOrderStatus.DEALING);
        searchModel.setBatchId(safePayBatchId);
        long dealingCount = safePayOrderManager.searchCount(searchModel);
        if (dealingCount > 0) {
            return;
        }

        SafePayBatch batch = safePayBatchManager.getById(safePayBatchId);

        processOnFinalTrans(batch);

    }

    @Override
    public void updateDealingSafePayOrder(Long safePayOrderId) {
        log.info("updateDealingSafePayOrder orderId={}", safePayOrderId);
        SafePayOrder order = safePayOrderManager.getById(safePayOrderId);
        if (order.getStatus() != SafePayOrderStatus.DEALING) {
            return;
        }

        queryOrderTransResult(order, false);
        if (order.getStatus() == SafePayOrderStatus.DEALING) {
            return;
        }
        order.setEndTime(LocalDateTime.now());
        safePayOrderManager.updateByPrimaryKeyWithVersion(order);
        log.info("updateDealingSafePayOrder success order={}", SerializationUtil.obj2String(order));
    }


    private void processOnBankTrans(SafePayBatch safePayBatch) {
        //重新查出来
        List<SafePayOrder> orders = safePayOrderManager.listByBatchId(safePayBatch.getUserId(), safePayBatch.getId());
        long failedCount = 0L;
        for (SafePayOrder order : orders) {
            if (order.getStatus() == SafePayOrderStatus.FAIL) {
                failedCount++;
            }
        }

        if (failedCount == safePayBatch.getTotalCount()) {
            //全部失败了 直接可以结算了
            safePayBatch.setStatus(SafePayBatchStatus.FAIL);
            //资金结算
            BigDecimal successAmount = new BigDecimal("0.00");
            BigDecimal failedAmount = new BigDecimal("0.00");
            long successCount = 0L;
            failedCount = 0L;

            for (SafePayOrder order : orders) {
                if (order.getStatus() == SafePayOrderStatus.SUCCESS) {
                    successAmount = successAmount.add(order.getTransAmount());
                    successCount++;
                } else {
                    failedAmount = failedAmount.add(order.getTransAmount());
                    failedCount++;
                }
            }
            safePayBatch.setSuccessAmount(successAmount);
            safePayBatch.setSuccessCount(successCount);
            safePayBatch.setFailedAmount(failedAmount);
            safePayBatch.setFailCount(failedCount);

            //资金结算 收益 退款等
            safePayInnerService.settleBatchBenefit(safePayBatch);
            return;
        } else {
            safePayBatch.setStatus(SafePayBatchStatus.DEALING);
        }

        safePayBatchManager.updateByPrimaryKeyWithVersion(safePayBatch);
    }

    private void processOnFinalTrans(SafePayBatch safePayBatch) {
        //重新查出来
        List<SafePayOrder> orders = safePayOrderManager.listByBatchId(safePayBatch.getUserId(), safePayBatch.getId());
        //查询一下有没有处理中的（一般不会出现） 一直查询 直到全部跟新完
        for (SafePayOrder order : orders) {
            if (order.getStatus() == SafePayOrderStatus.DEALING) {
                noticeHelper.sendSystemMonitorNotice("安全付单笔转账正在处理中：" + order.getId(), "text");
                queryOrderTransResult(order, true);
                if (order.getStatus() != SafePayOrderStatus.DEALING) {
                    order.setEndTime(LocalDateTime.now());
                    safePayOrderManager.updateByPrimaryKeyWithVersion(order);
                }
            }
        }

        //资金结算
        BigDecimal successAmount = new BigDecimal("0.00");
        BigDecimal failedAmount = new BigDecimal("0.00");
        long successCount = 0L;
        long failedCount = 0L;

        for (SafePayOrder order : orders) {
            if (order.getStatus() == SafePayOrderStatus.SUCCESS) {
                successAmount = successAmount.add(order.getTransAmount());
                successCount++;
            } else {
                failedAmount = failedAmount.add(order.getTransAmount());
                failedCount++;
            }
        }
        safePayBatch.setSuccessAmount(successAmount);
        safePayBatch.setSuccessCount(successCount);
        safePayBatch.setFailedAmount(failedAmount);
        safePayBatch.setFailCount(failedCount);

        //资金结算 收益 退款等
        safePayInnerService.settleBatchBenefit(safePayBatch);
    }

    @Override
    @WriteTransactional
    public void sendCheckCode(User user, Long batchId, String ip, Integer useSpareReceiveMobile) {
        UserAccount userAccount = userAccountManager.getByIdWithLock(user.getId());
        if (user.getServiceUnitPrice() != 0L && userAccount.getTransBatchLeft() <= 0L && userAccount.getMoneyLeft() < CoreConstant.TRANS_CHECK_CODE_FEE) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "余额不足");
        }
        SafePayBatch batch = safePayBatchManager.findById(user.getId(), batchId);
        if (batch == null) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "批次不存在");
        }

        UserProfile profile = userProfileManager.getById(user.getId());
        String mobile = user.getMobile();
        if (!Strings.isNullOrEmpty(profile.getReceiveMobile())) {
            mobile = profile.getReceiveMobile();
        }
        if (useSpareReceiveMobile != null && useSpareReceiveMobile == 1 && !Strings.isNullOrEmpty(profile.getSpareReceiveMobile())) {
            mobile = profile.getSpareReceiveMobile();
        }
        SafePayBookAccount safePayBookAccount = safePayBookAccountManager.findByAlipayNickName(user.getId(), batch.getAlipayNickName());
        if (!Strings.isNullOrEmpty(safePayBookAccount.getReceiveMobile())) {
            mobile = safePayBookAccount.getReceiveMobile();
        }

        CheckCode checkCode = checkCodeService.sendCheckCodeWithRelatedId(mobile, CheckCodeType.ENSURE_SAFE_PAY_BATCH, ip, batchId);
        //预扣服务费 有些自用的账号不扣短信费用
        if (user.getServiceUnitPrice() != 0L && userAccount.getTransBatchLeft() <= 0L) {
            AccountDetail accountDetail = AccountDetail.getInstance(user.getId(), checkCode.getId());
            accountDetailHelper.initCommonAccountDetail(accountDetail, AccountBillType.CHECK_CODE, -CoreConstant.TRANS_CHECK_CODE_FEE, "安全付确认转账短信验证码");
            accountService.onPay(user, userAccount, accountDetail);
        }
    }

    @Override
    @WriteTransactional
    public void closeSafePayBatch(User user, String importBatchNo) {
        UserAccount userAccount = userAccountManager.getByIdWithLock(user.getId());
        if (Strings.isNullOrEmpty(importBatchNo)) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "批次号不能为空");
        }

        SafePayBatch batch = safePayBatchManager.findByImportBatchNo(user.getId(), importBatchNo);
        if (batch == null) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "批次不存在");
        }
        if (batch.getStatus() != SafePayBatchStatus.WAIT_ENSURE) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "当前批次已被处理不能关闭");
        }

        batch.setStatus(SafePayBatchStatus.DISUSE);
        batch.setEndTime(LocalDateTime.now());
        batch.setRefund(batch.getServicePrice());
        batch.setFinalServicePrice(0L);
        safePayBatchManager.updateByPrimaryKeyWithVersion(batch);

        safePayOrderManager.updateStatusByBatchId(user.getId(), batch.getId(), SafePayOrderStatus.DISUSE, batch.getFree(), LocalDateTime.now());

        refundServiceMoney(user, userAccount, batch);
    }

    @Override
    @WriteTransactional
    public void closeSafePayBatchById(User user, Long id) {
        UserAccount userAccount = userAccountManager.getByIdWithLock(user.getId());
        if (id == null) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "批次号不能为空");
        }

        SafePayBatch batch = safePayBatchManager.findById(user.getId(), id);
        if (batch == null) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "批次不存在");
        }
        if (batch.getStatus() != SafePayBatchStatus.WAIT_ENSURE) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "当前批次已被处理不能关闭");
        }

        batch.setStatus(SafePayBatchStatus.DISUSE);
        batch.setEndTime(LocalDateTime.now());
        batch.setRefund(batch.getServicePrice());
        batch.setFinalServicePrice(0L);
        safePayBatchManager.updateByPrimaryKeyWithVersion(batch);

        safePayOrderManager.updateStatusByBatchId(user.getId(), batch.getId(), SafePayOrderStatus.DISUSE, batch.getFree(), LocalDateTime.now());

        refundServiceMoney(user, userAccount, batch);

        processCloseRelatedBusiness(user, batch);
    }

    private void processCloseRelatedBusiness(User user, SafePayBatch batch) {
        //安全付收集到卡
        if (batch.getImportBatchNo().startsWith(TransBizTypeConstant.CB_SP)) {
            collectionSafePayService.closeCollectionSafePayBatchCallback(user, batch);
        }
    }

    @Override
    public void updateSafePayOrder(Long id, SafePayOrderStatus status, String errorMsg, String errorCode) {
        log.info("updateSafePayOrder id={}|status={}", id, status);
        SafePayOrder order = safePayOrderManager.getById(id);
        if (order.getEndTime() != null && SafePayOrderStatus.REFUND != status) {
            return;
        }
        //只处理银行卡的
        if (!IdentityType.BANKCARD_ACCOUNT.getValue().equals(order.getIdentityType())) {
            return;
        }

        order.setStatus(status);
        order.setEndTime(LocalDateTime.now());
        order.setErrorMsg(errorMsg);
        order.setErrorCode(errorCode);

        safePayOrderManager.updateByPrimaryKeyWithVersion(order);
    }

    @Override
    public SafePayBatch manualImportSafePayTrans(User user, ManualSafePayTransModel transModel) {
        SafePayBatchCreateModel batchModel = transformSafePayBatch(transModel);
        UserProfile userProfile = userProfileManager.getById(user.getId());
        if (Boolean.TRUE.equals(userProfile.getBatchEnsure())) {
            batchModel.setBatchEnsure(true);
        }
        return createSafePayBatch(user, batchModel);
    }

    @Override
    public void updateSafePayAccount(String outBizNo, String agreementNo, String extCardInfo, String accountBookId) {
        SafePayBookAccount safePayBookAccount = safePayBookAccountManager.findByExternalAgreementNo(outBizNo);
        if (safePayBookAccount == null) {
            log.warn("updateSafePayAccount error can not find account|outBizNo={}", outBizNo);
            return;
        }

        if (!Strings.isNullOrEmpty(safePayBookAccount.getAgreementNo()) || !Strings.isNullOrEmpty(safePayBookAccount.getBookId())) {
            //保存下历史快照
            log.warn("saveSafePayBookAccount save snap account={}", SerializationUtil.obj2String(safePayBookAccount));
            DataSnap dataSnap = DataSnap.getInstance();
            dataSnap.setData(SerializationUtil.obj2String(safePayBookAccount));
            dataSnap.setType(DataSnapType.SAFE_PAY_BOOK_ACCOUNT);
            dataSnap.setRelatedId(safePayBookAccount.getId());
            dataSnapManager.save(dataSnap);
        }


        safePayBookAccount.setAgreementNo(agreementNo);
        safePayBookAccount.setBookId(accountBookId);
        safePayBookAccount.setExtCardInfo(extCardInfo);
        safePayBookAccount.setSignStatus(SafePayBookAccountSignStatus.NORMAL);
        safePayBookAccount.setSignTime(LocalDateUtil.getDateTimeAsString(LocalDateTime.now()));
        safePayBookAccount.setBookStatus(SafePayBookAccountBookStatus.YES);
        safePayBookAccountManager.updateByPrimaryKeyWithVersion(safePayBookAccount);

        //失败重试30秒
        LocalDateTime deadline = LocalDateTime.now().plusSeconds(30L);
        long t = System.currentTimeMillis();
        while (LocalDateTime.now().isBefore(deadline)) {
            try {
                //订阅同名银行卡入金消息
                AlipayFundAccountbookNotifySubscribeResponse subscribeResponse = xpaySafePayAlipayClient.alipayFundAccountbookNotifySubscribe(safePayBookAccount.getBookId(), safePayBookAccount.getAgreementNo());
                if (subscribeResponse.isSuccess() && "VALID".equals(subscribeResponse.getSubscribeStatus())) {
                    log.info("saveSafePayBookAccount subscribe success|cost={}|externalAgreementNo={}", System.currentTimeMillis() - t, outBizNo);
                    return;
                } else {
                    log.info("saveSafePayBookAccount subscribe failed|externalAgreementNo={}", outBizNo);
                }
            } catch (AlipayApiException e) {
                log.warn("saveSafePayBookAccount create book failed|externalAgreementNo={}", outBizNo, e);
            }
            ThreadUtil.safeSleep(2000L, "saveSafePayBookAccount subscribe sleep");
        }

        log.info("saveSafePayBookAccount subscribe final failed|externalAgreementNo={}", outBizNo);

    }

    @Override
    public String getSignPage(User user, SafePayPageSignModel signModel) {
        UserProfile userProfile = userProfileManager.getById(user.getId());
        if (signModel.getAlipayNickName().contains(" ")) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "支付宝账号不能包含空格");
        }

        if (!signModel.getAlipayNickName().contains("@")) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "请使用企业支付宝签约");
        }
        if (Strings.isNullOrEmpty(signModel.getAlipayNickName())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "请输入支付宝账号");
        }
        if (Strings.isNullOrEmpty(signModel.getAlipayName())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "请输入支付宝企业名称");
        }
        if (!signModel.getAlipayName().equals(user.getName())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "支付宝企业名称与商户账号主体不一致");
        }
        signModel.setAlipayNickName(signModel.getAlipayNickName().trim());
        signModel.setAlipayName(signModel.getAlipayName().trim());
        if (!AlipayAccountCheckUtil.isEffectiveAccount(signModel.getAlipayNickName())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "支付宝账号格式错误");
        }
        if (!Boolean.TRUE.equals(userProfile.getOpenSafePay())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "尚未开通安全付功能");
        }

        if (Boolean.TRUE.equals(userProfile.getOpenSafePayNewSignMethod())) {
            return getSignPage2(user, signModel);
        } else {
            return getSignPage1(user, signModel);
        }

    }

    private String getSignPage1(User user, SafePayPageSignModel signModel) {
        String alipayNickName = signModel.getAlipayNickName();
        Long userId = user.getId();
        //先从缓存中查，如果没有再请求新的
        Object authUrl = redisHelper.get(RedisKey.SAFE_PAY_SIGN_URL + ":" + user.getId() + ":" + alipayNickName);
        if (authUrl != null) {
            return authUrl.toString();
        }

        String externalAgreementNo = userId + "-" + System.currentTimeMillis();

        String authPageUrl = xpaySafePayAlipayClient.alipayUserAgreementPageSignCreate(externalAgreementNo, alipayNickName);

        SafePayBookAccount safePayBookAccount = safePayBookAccountManager.findByAlipayNickName(userId, alipayNickName);
        if (safePayBookAccount == null) {
            safePayBookAccount = SafePayBookAccount.getInstance();
            safePayBookAccount.setUserId(userId);
            safePayBookAccount.setAlipayNickName(alipayNickName);
            safePayBookAccount.setAlipayName(signModel.getAlipayName());
            safePayBookAccount.setExternalAgreementNo(externalAgreementNo);
            safePayBookAccount.setSignStatus(SafePayBookAccountSignStatus.TEMP);
            safePayBookAccount.setBookStatus(SafePayBookAccountBookStatus.NO);
            safePayBookAccount.setAuditStatus(SafePayBookAccountAuditStatus.INIT);
            safePayBookAccount.setSignMethod(SafePayBookAccountSignMethod.OLD);
            safePayBookAccount.setReceiveMobile(signModel.getReceiveMobile());
            safePayBookAccountManager.save(safePayBookAccount);
        } else {
            log.warn("doGetSignPage safePayBookAccount before={}|alipayNickName={}", SerializationUtil.obj2String(safePayBookAccount), alipayNickName);
            if (safePayBookAccount.getSignStatus().equals(SafePayBookAccountSignStatus.NORMAL)) {
                throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "已签约，请勿重复签约");
            }
            safePayBookAccount.setAlipayName(signModel.getAlipayName());
            safePayBookAccount.setExternalAgreementNo(externalAgreementNo);
            safePayBookAccount.setSignMethod(SafePayBookAccountSignMethod.OLD);
            safePayBookAccountManager.updateByPrimaryKeyWithVersion(safePayBookAccount);
        }
        redisHelper.set(RedisKey.SAFE_PAY_SIGN_URL + ":" + user.getId() + ":" + alipayNickName, authPageUrl, 60L);
        return authPageUrl;
    }

    @Override
    public String getSignPage2(User user, SafePayPageSignModel signModel) {
        Long userId = user.getId();
        //先从缓存中查，如果没有再请求新的
        Object authUrl = redisHelper.get(RedisKey.SAFE_PAY_SIGN_2_URL + ":" + user.getId() + ":" + signModel.getAlipayName() + ":" + signModel.getAlipayNickName());
        if (authUrl != null) {
            return authUrl.toString();
        }

        String alipayNickName = signModel.getAlipayNickName();
        SafePayBookAccount safePayBookAccount = safePayBookAccountManager.findByAlipayNickName(userId, alipayNickName);
        if (safePayBookAccount != null) {
            log.warn("doGetSignPage safePayBookAccount before={}|alipayNickName={}", SerializationUtil.obj2String(safePayBookAccount), alipayNickName);
            if (safePayBookAccount.getSignStatus().equals(SafePayBookAccountSignStatus.NORMAL)) {
                throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "已签约，请勿重复签约");
            }

            AlipayFundAccountbookPageCreateModel model = new AlipayFundAccountbookPageCreateModel();

            String externalAgreementNo = user.getId() + "-" + System.currentTimeMillis();
            model.setOutBizNo(externalAgreementNo);
            model.setAccountBookAlias("我的记账本");
            AlipayFundAccountbookPageCreateModel.PrincipalInfo principalInfo = new AlipayFundAccountbookPageCreateModel.PrincipalInfo();
            principalInfo.setIdentity(signModel.getAlipayNickName());
            principalInfo.setIdentityType(IdentityType.ALIPAY_LOGON_ID.getValue());
            principalInfo.setIdentityName(signModel.getAlipayName());
            model.setPrincipalInfo(principalInfo);

            String authPageUrl = xpaySafePayAlipayClient.alipayFundAccountbookPageCreate(model);
            log.info("getSignPage2 alipayNickName={}|authPageUrl={}", alipayNickName, authPageUrl);
            safePayBookAccount.setAlipayName(signModel.getAlipayName());
            safePayBookAccount.setExternalAgreementNo(externalAgreementNo);
            safePayBookAccount.setSignMethod(SafePayBookAccountSignMethod.NEW);
            safePayBookAccountManager.updateByPrimaryKeyWithVersion(safePayBookAccount);

            redisHelper.set(RedisKey.SAFE_PAY_SIGN_2_URL + ":" + user.getId() + ":" + signModel.getAlipayName() + ":" + signModel.getAlipayNickName(), authPageUrl, 24 * 60 *60L);
            return authPageUrl;
        } else {
            AlipayFundAccountbookPageCreateModel model = new AlipayFundAccountbookPageCreateModel();
            String externalAgreementNo = user.getId() + "-" + System.currentTimeMillis();
            model.setOutBizNo(externalAgreementNo);
            model.setAccountBookAlias("我的记账本");
            AlipayFundAccountbookPageCreateModel.PrincipalInfo principalInfo = new AlipayFundAccountbookPageCreateModel.PrincipalInfo();
            principalInfo.setIdentity(signModel.getAlipayNickName());
            principalInfo.setIdentityType(IdentityType.ALIPAY_LOGON_ID.getValue());
            principalInfo.setIdentityName(signModel.getAlipayName());
            model.setPrincipalInfo(principalInfo);

            String authPageUrl = xpaySafePayAlipayClient.alipayFundAccountbookPageCreate(model);
            log.info("getSignPage2 alipayNickName={}|authPageUrl={}", alipayNickName, authPageUrl);
            safePayBookAccount = SafePayBookAccount.getInstance();
            safePayBookAccount.setUserId(userId);
            safePayBookAccount.setAlipayNickName(alipayNickName);
            safePayBookAccount.setAlipayName(signModel.getAlipayName());
            safePayBookAccount.setReceiveMobile(signModel.getReceiveMobile());
            safePayBookAccount.setExternalAgreementNo(externalAgreementNo);
            safePayBookAccount.setSignStatus(SafePayBookAccountSignStatus.TEMP);
            safePayBookAccount.setBookStatus(SafePayBookAccountBookStatus.NO);
            safePayBookAccount.setAuditStatus(SafePayBookAccountAuditStatus.INIT);
            safePayBookAccount.setSignMethod(SafePayBookAccountSignMethod.NEW);
            safePayBookAccountManager.save(safePayBookAccount);
            redisHelper.set(RedisKey.SAFE_PAY_SIGN_2_URL + ":" + user.getId() + ":" + signModel.getAlipayName() + ":" + signModel.getAlipayNickName(), authPageUrl, 24 * 60 *60L);
            return authPageUrl;
        }
    }

    @Override
    public BookAccountTransferOrder transferBookAccount(User user, BookAccountTransferOrderModel model) {
        bookAccountTransferOrderValidator.validateBookTransfer(user, model);

        SafePayBookAccount payerAlipayAccount = safePayBookAccountManager.findByAlipayNickName(user.getId(), model.getPayerAlipayAccount());
        if (payerAlipayAccount == null) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "付款方支付宝不存在");
        }
        if (payerAlipayAccount.getSignStatus() != SafePayBookAccountSignStatus.NORMAL) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "付款方支付宝未签约");
        }
        if (payerAlipayAccount.getBookStatus() != SafePayBookAccountBookStatus.YES) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "付款方支付宝未开通");
        }
        if (payerAlipayAccount.getAuditStatus() != SafePayBookAccountAuditStatus.NORMAL) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "付款方支付宝未实名认证");
        }

        SafePayBookAccount payeeAlipayAccount = safePayBookAccountManager.findByAlipayNickName(user.getId(), model.getPayeeAlipayAccount());
        if (payeeAlipayAccount == null) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "收款方支付宝不存在");
        }
        if (payeeAlipayAccount.getSignStatus() != SafePayBookAccountSignStatus.NORMAL) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "收款方支付宝未签约");
        }
        if (payeeAlipayAccount.getBookStatus() != SafePayBookAccountBookStatus.YES) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "收款方支付宝未开通");
        }
        if (payeeAlipayAccount.getAuditStatus() != SafePayBookAccountAuditStatus.NORMAL) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "收款方支付宝未实名认证");
        }

        if (model.getPayeeAlipayAccount().equals(model.getPayerAlipayAccount())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "付款方和收款方支付宝账号不能相同");
        }

        BookAccountTransferOrder selectedOrder = bookAccountTransferOrderManager.findByImportBizNo(user.getId(), model.getImportBizNo());
        if (selectedOrder != null) {
            if (selectedOrder.getStatus() == UniTransStatus.SUCCESS) {
                if (selectedOrder.getTransAmount().compareTo(new BigDecimal(model.getTransAmount())) != 0
                        || !selectedOrder.getPayerAlipayAccount().equals(model.getPayerAlipayAccount())
                        || !selectedOrder.getPayeeAlipayAccount().equals(model.getPayeeAlipayAccount())
                ) {
                    throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "外部单号已被使用");
                }
                return selectedOrder;
            }
            selectedOrder.setUserId(user.getId());
            selectedOrder.setPayerSafePayBookAccountId(payerAlipayAccount.getId());
            selectedOrder.setPayerAlipayAccount(payerAlipayAccount.getAlipayNickName());
            selectedOrder.setPayerAgreementNo(payerAlipayAccount.getAgreementNo());
            selectedOrder.setPayerBookId(payerAlipayAccount.getBookId());
            selectedOrder.setPayeeSafePayBookAccountId(payeeAlipayAccount.getId());
            selectedOrder.setPayeeAlipayAccount(payeeAlipayAccount.getAlipayNickName());
            selectedOrder.setPayeeAgreementNo(payeeAlipayAccount.getAgreementNo());
            selectedOrder.setPayeeBookId(payeeAlipayAccount.getBookId());
            selectedOrder.setOrderTitle(model.getOrderTitle());
            selectedOrder.setRemark(model.getRemark());
            selectedOrder.setTransAmount(new BigDecimal(model.getTransAmount()));
            selectedOrder.setStatus(UniTransStatus.WAIT_TRANS);
            selectedOrder.setStartTime(LocalDateTime.now());
            bookAccountTransferOrderManager.update(selectedOrder);
            return doTransferBookAccount(selectedOrder);
        } else {
            BookAccountTransferOrder order = new BookAccountTransferOrder();

            order.setUserId(user.getId());
            order.setOutBizNo(GenIdUtil.getId("B", user.getId()));
            order.setImportBizNo(model.getImportBizNo());
            order.setPayerSafePayBookAccountId(payerAlipayAccount.getId());
            order.setPayerAlipayAccount(payerAlipayAccount.getAlipayNickName());
            order.setPayerAgreementNo(payerAlipayAccount.getAgreementNo());
            order.setPayerBookId(payerAlipayAccount.getBookId());
            order.setPayeeSafePayBookAccountId(payeeAlipayAccount.getId());
            order.setPayeeAlipayAccount(payeeAlipayAccount.getAlipayNickName());
            order.setPayeeAgreementNo(payeeAlipayAccount.getAgreementNo());
            order.setPayeeBookId(payeeAlipayAccount.getBookId());
            order.setOrderTitle(model.getOrderTitle());
            order.setRemark(model.getRemark());
            order.setTransAmount(new BigDecimal(model.getTransAmount()));
            order.setStatus(UniTransStatus.WAIT_TRANS);
            order.setStartTime(LocalDateTime.now());
            bookAccountTransferOrderManager.save(order);
            return doTransferBookAccount(order);
        }
    }

    @Override
    public void notifyTransferBookAccountSuccess(String outBizNo, String payFundOrderId, String orderId) {
        log.info("notifyTransferBookAccountSuccess outBizNo={}|payFundOrderId={}|orderId={}", outBizNo, payFundOrderId, orderId);

        BookAccountTransferOrder order = bookAccountTransferOrderManager.findByOutBizNo(outBizNo);

        if (order == null) {
            log.error("notifyTransferBookAccountSuccess order not exist|outBizNo={}", outBizNo);
            return;
        }
        if (order.getStatus() == UniTransStatus.SUCCESS) {
            return;
        }

        order.setPayFundOrderId(payFundOrderId);
        order.setOrderId(orderId);
        order.setTransDate(order.getStartTime());
        order.setEndTime(LocalDateTime.now());
        bookAccountTransferOrderManager.update(order);
    }

    private BookAccountTransferOrder doTransferBookAccount(BookAccountTransferOrder order) {
        //调拨
        try {
            AlipayFundTransUniTransferResponse resp = xpaySafePayAlipayClient.uniTransferAlipayFundTransForBook(order);
            if (!resp.isSuccess() || !"SUCCESS".equals(resp.getStatus())) {
                bookAccountTransferOrderManager.deleteById(order.getId());
                log.info("transferBookAccount error, orderId={}|error={}", resp.getCode() + "|" + resp.getSubCode(), resp.getMsg() + "|" + resp.getSubMsg());
                throw new BusinessException(resp.getSubCode(), resp.getSubMsg());
            }
            order.setOrderId(resp.getOrderId());
            order.setPayFundOrderId(resp.getPayFundOrderId());
            order.setStatus(UniTransStatus.SUCCESS);
            order.setTransDate(LocalDateUtil.parseStringToDateTime(resp.getTransDate()));
            order.setEndTime(LocalDateTime.now());
            order.setErrorMsg(resp.getSubMsg());
            order.setErrorCode(resp.getSubCode());
            bookAccountTransferOrderManager.update(order);
            return order;
        } catch (AlipayApiException e) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "支付宝接口异常");
        }
    }

    //转换为SafePayBatchCreate
    private SafePayBatchCreateModel transformSafePayBatch(ManualSafePayTransModel transModel) {
        if (Strings.isNullOrEmpty(transModel.getName())) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "请输入收款人真实姓名");
        }
        if (transModel.getType() == null) {
            transModel.setType(PayeeType.BANK);
        }

        if (Strings.isNullOrEmpty(transModel.getIdentity())) {
            if (transModel.getType() == PayeeType.ALIPAY) {
                throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "请输入收款人支付宝账号");
            } else {
                throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "请输入收款人银行卡号");
            }
        }

        SafePayBatchCreateModel batchModel = new SafePayBatchCreateModel();
        batchModel.setAlipayNickName(transModel.getAlipayNickName());
        batchModel.setType(transModel.getType());
        batchModel.setTotalTransAmount(transModel.getTransAmount());
        batchModel.setNeedNotify(false);
        batchModel.setTotalCount(1L);
        //自动生成批次号
        String batchId = TransBizTypeConstant.AUTO + System.currentTimeMillis();
        batchModel.setImportBatchNo(batchId);

        List<SafePayTransModel> list = new ArrayList<>();
        SafePayTransModel safePayTransModel = new SafePayTransModel();
        safePayTransModel.setImportBizNo("1");
        safePayTransModel.setName(transModel.getName().trim());
        safePayTransModel.setIdentity(transModel.getIdentity().trim().replace(" ", ""));
        safePayTransModel.setTransAmount(transModel.getTransAmount());
        safePayTransModel.setRemark(transModel.getRemark());
        list.add(safePayTransModel);
        safePayTransModel.setOrderTitle(transModel.getOrderTitle());

        batchModel.setPayTrans(list);
        return batchModel;
    }

    //退款
    private void refundServiceMoney(User user, UserAccount userAccount, SafePayBatch batch) {
        //免费单不用退款
        if (batch.getFree()) {
            log.info("safe pay refundServiceMoney but batch is free|batchId={}", batch.getId());
            return;
        }
        //查询历史有没有退过
        AccountDetail selected = accountDetailManager.findByRelatedId(user.getId(), batch.getId(), AccountBillType.PAY_TRANS_SERVICE_PRICE_REFUND);
        if (selected != null) {
            log.warn("safe pay refundServiceMoney account detail exist batch={}", SerializationUtil.obj2String(batch));
            return;
        }

        AccountDetail accountDetail = AccountDetail.getInstance(user.getId(), batch.getId());
        accountDetailHelper.initCommonAccountDetail(accountDetail, AccountBillType.SAFE_PAY_TRANS_SERVICE_PRICE_REFUND, batch.getServicePrice(), "");

        accountService.onPay(user, userAccount, accountDetail);
    }

    private void queryOrderTransResult(SafePayOrder order, boolean queryUtilSuccess) {
        long startTime = System.currentTimeMillis();
        log.info("queryOrderTransResult start|orderId={}|outBizNo={}", order.getId(), order.getOutBizNo());

        long errorCount = 0L;
        long queryCount = 0L;
        //请求发生错误最多重试10次
        while (errorCount <= 10L) {
            queryCount++;
            log.info("queryOrderTransResult post|orderId={}|outBizNo={}|queryCount={}", order.getId(), order.getOutBizNo(), queryCount);
            AlipayFundTransCommonQueryResponse resp;
            try {
                resp = xpaySafePayAlipayClient.queryTransferAlipayFundTrans(order.getOutBizNo());
            } catch (AlipayApiException e) {
                errorCount++;
                ThreadUtil.safeSleep(1000, "");
                continue;
            }
            if (resp.isSuccess()) {
                if (SafePayOrderStatus.of(resp.getStatus()) == SafePayOrderStatus.DEALING) {
                    if (queryCount > 10) {
                        noticeHelper.sendSystemInfoNotice("安全付订单支付状态处于处理中查询次数超过10次，任然在查询中|" + order.getId(), "text");
                        if (!queryUtilSuccess) {
                            break;
                        }
                    }
                    ThreadUtil.safeSleep(1000, "");
                    continue;
                }
                order.setOrderId(resp.getOrderId());
                order.setPayFundOrderId(resp.getPayFundOrderId());
                order.setStatus(SafePayOrderStatus.of(resp.getStatus()));
                order.setTransDate(resp.getPayDate());
                order.setErrorMsg(resp.getFailReason());
                order.setErrorCode(resp.getErrorCode());
                break;
            } else {
                errorCount++;
                ThreadUtil.safeSleep(1000, "");
            }
        }
        long cost = System.currentTimeMillis() - startTime;
        if (cost > 2000) {
            noticeHelper.sendSystemInfoNotice("安全付订单支付状态处于处理中时间超过2秒|" + order.getId(), "text");
        }
        log.info("queryOrderTransResult end|orderId={}|outBizNo={}|cost={}", order.getId(), order.getOutBizNo(), cost);
    }

    private void transOrder(SafePayOrder order) throws AlipayApiException {
        AlipayFundTransUniTransferResponse resp;
        long t = System.currentTimeMillis();
        if (IdentityType.BANKCARD_ACCOUNT.getValue().equals(order.getIdentityType())) {
            resp = xpaySafePayAlipayClient.uniTransferAlipayToBankFundTrans(order);
        } else {
            resp = xpaySafePayAlipayClient.uniTransferAlipayFundTrans(order);
        }
        log.info("transOrder orderId={}|cost={}|resp={}", order.getId(), System.currentTimeMillis() - t, SerializationUtil.obj2String(resp));
        if (resp.isSuccess()) {
            order.setOrderId(resp.getOrderId());
            order.setPayFundOrderId(resp.getPayFundOrderId());
            order.setStatus(SafePayOrderStatus.of(resp.getStatus()));
            order.setTransDate(resp.getTransDate());
        } else {
            log.info("transOrder request no success orderId={}|resp={}", order.getId(), SerializationUtil.obj2String(resp));
            //这里得分什么错误，如果是已经转过的，但是意外没有跟新掉数据库的订单状态 查询下实际的转账状态
            AlipayFundTransCommonQueryResponse ret = xpaySafePayAlipayClient.queryTransferAlipayFundTrans(order.getOutBizNo());
            log.info("transOrder request no success then query actual result orderId orderId={}|ret={}", order.getId(), SerializationUtil.obj2String(ret));
            if (ret.isSuccess() && ret.getStatus() != null && ret.getOrderId() != null) {
                //说明这个已经转过了
                order.setOrderId(ret.getOrderId());
                order.setPayFundOrderId(ret.getPayFundOrderId());
                order.setStatus(SafePayOrderStatus.of(ret.getStatus()));
                order.setTransDate(ret.getPayDate());
            } else {
                order.setStatus(SafePayOrderStatus.FAIL);
                order.setErrorCode(resp.getSubCode());
                order.setErrorMsg(resp.getSubMsg());
            }
        }
    }

    private long checkHasEnoughMoneyAndReturnEstimateFee(UserAccount userAccount, User user, SafePayBatchCreateModel model) {
        if (userAccount.getTransBatchLeft() > 0 && new BigDecimal(model.getTotalTransAmount()).compareTo(CoreConstant.MAX_TRANS_FREE_AMOUNT) <= 0) {
            return 0L;
        }

        //预估服务费 分
        long estimateFee;

        if (ServicePayMode.FIXED_AMOUNT.equals(user.getMode())) {
            estimateFee = user.getServiceUnitPrice() * model.getPayTrans().size();
            if (PayeeType.BANK.equals(model.getType()) && user.getBankServiceUnitPrice() != null) {
                estimateFee = user.getBankServiceUnitPrice() * model.getPayTrans().size();
            }
        } else if (ServicePayMode.FIXED_RATIO.equals(user.getMode())) {
            BigDecimal totalMoney = StreamUtil.sum(model.getPayTrans(), (Function<SafePayTransModel, BigDecimal>) safePayTransModel -> new BigDecimal(safePayTransModel.getTransAmount()));

            BigDecimal serviceRatio = user.getServiceRatio();
            if (PayeeType.BANK.equals(model.getType()) && user.getBankServiceRatio() != null) {
                serviceRatio = user.getBankServiceRatio();
            }
            estimateFee = totalMoney.multiply(serviceRatio).multiply(new BigDecimal(100)).setScale(0, RoundingMode.UP).longValue();

        } else {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "系统错误");
        }

        if (estimateFee > userAccount.getMoneyLeft()) {
            throw new BusinessException(PoseidonErrorCode.PARAMETER_ILLEGAL, "服务费余额不足，请先充值服务费");
        }

        return estimateFee;
    }
}
